﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

using RexToy.Logging;
using RexToy.ExpressionLanguage;
using RexToy.UI.WinForm.Pmlx;

namespace RexToy.UI.WinForm.ViewModel
{
    public interface IModelOwner
    {
        ViewModelBase ViewModel { get; }
    }

    public static class DataExchangeModelOwnerExtension
    {
        private static ILog _log = LogContext.GetLogger<IModelOwner>();

        private static bool UpdateModel(this IModelOwner owner)
        {
            if (owner.ViewModel != null)
            {
                if (owner is Form)
                {
                    ((Form)owner).UpdateModel(owner.ViewModel);
                    return true;
                }

                if (owner is UserControl)
                {
                    ((UserControl)owner).UpdateModel(owner.ViewModel);
                    return true;
                }
            }
            return false;
        }

        private static bool UpdateView(this IModelOwner owner)
        {
            if (owner.ViewModel != null)
            {
                if (owner is Form)
                {
                    ((Form)owner).UpdateView(owner.ViewModel);
                    return true;
                }

                if (owner is UserControl)
                {
                    ((UserControl)owner).UpdateView(owner.ViewModel);
                    return true;
                }
            }
            return false;
        }

        public static void UpdateModel(this Form f, ViewModelBase model)
        {
            _UpdateModel(f, model);
        }

        public static void UpdateView(this Form f, ViewModelBase model)
        {
            _UpdateView(f, model);
            UpdateDelayComputeControls(f);
        }

        public static void UpdateModel(this UserControl uc, ViewModelBase model)
        {
            _UpdateModel(uc, model);
        }

        public static void UpdateView(this UserControl uc, ViewModelBase model)
        {
            _UpdateView(uc, model);
            UpdateDelayComputeControls(uc);
        }

        private static void _UpdateModel(Control c, ViewModelBase model)
        {
            foreach (Control sub in c.Controls)
            {
                if (sub is IModelOwner)
                {
                    IModelOwner owner = sub as IModelOwner;
                    if (owner.UpdateModel())
                        continue;
                }

                IDataExchange de = sub as IDataExchange;
                if (de != null && de.Exchange)
                {
                    de.UpdateModel(model);
                }
                else
                    _UpdateModel(sub, model);
            }
        }

        private static void _UpdateView(Control c, ViewModelBase model)
        {
            foreach (Control sub in c.Controls)
            {
                if (sub is IModelOwner)
                {
                    IModelOwner owner = sub as IModelOwner;
                    if (owner.UpdateView())
                        continue;
                }

                IDataExchange de = sub as IDataExchange;
                if (de != null && de.Exchange)
                {
                    de.UpdateView(model);
                }
                else
                    _UpdateView(sub, model);
            }
        }

        private static void UpdateDelayComputeControls(Control c)
        {
            _log.Debug("Update view, eval delay computed items.");

            IPmlx pmlx = c as IPmlx;
            if (pmlx == null)
                return;

            foreach (var meta in pmlx.DelayComputedControls)
            {
                IReflector r = Reflector.Bind(meta.Control);
                var engine = pmlx.PmlxDocument.ELEngine;
                foreach (var kvp in meta.DelayComputeAttribute)
                {
                    object val = engine.Eval(kvp.Value.UnBracketing(StringPair.Delayed_Template_Bracket));
                    Type pType = r.GetPropertyType(kvp.Key);
                    r.SetPropertyValue(kvp.Key, TypeCast.ChangeType(val, pType));
                }
            }

            _log.Debug("Eval delay computed items finish.");
        }
    }
}
